﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Globalization;
using System.Reflection;
using System.Threading;

namespace HiLand.Utility.Enums.OP
{
    [AttributeUsage(AttributeTargets.Field, AllowMultiple = true)]
    public class EnumItemDescriptionAttribute : Attribute
    {
        /// <summary>
        ///   A cache store, because accessing Attribute is according to reflection, it cost too much
        ///   So I add a cache, which based on special enum Type.
        /// </summary>
        private static readonly Dictionary<Type, Dictionary<object, Dictionary<string, EnumItemDescriptionAttribute>>> _cache =
            new Dictionary<Type, Dictionary<object, Dictionary<string, EnumItemDescriptionAttribute>>>();
        /**/
        /// <summary>
        ///   the culture of Description
        /// </summary>
        private string displaySerialName;
        /**/
        /// <summary>
        ///   the description content.
        /// </summary>
        private string description;


        /**/
        /// <summary>
        ///   Internal constructor.
        /// </summary>
        /// <param name="culture">special <see cref="CultureInfo"/></param>
        /// <param name="description">the content of Descroption</param>
        public EnumItemDescriptionAttribute(string displaySerialName, string description)
        {
            this.displaySerialName = displaySerialName;
            this.description = description;
        }

        /**/
        /// <summary>
        ///   Gets the <see cref="CultureInfo"/> for current decription.
        /// </summary>
        public string DisplaySerialName
        {
            get { return displaySerialName; }
        }

        /**/
        /// <summary>
        ///   Gets the description content.
        /// </summary>
        public string Description
        {
            get { return description; }
        }

        /**/
        /// <summary>
        ///   Gets all <see cref="EnumItemDescriptionAttribute"/>
        ///   for special enum value: <paramref name="value"/> and special enum type: <paramref name="enumType"/>.
        /// </summary>
        /// <param name="value">the value of special enum</param>
        /// <param name="enumType">the enum type of special enum</param>
        /// <returns>All attributes attatched on special enum value.</returns>
        public static Dictionary<string, EnumItemDescriptionAttribute> GetDisplayValues(object value, Type enumType)
        {
            if (enumType == null || !enumType.IsEnum)
            {
                throw new NotSupportedException("enumType is not a Enum");
            }
            if (value == null || !Enum.IsDefined(enumType, value))
            {
                throw new ArgumentException("value is not defined in " + enumType.FullName);
            }

            return GetDisplayValuesImp(value, enumType);
        }

        public static string GetDisplayValue(object value, Type enumType)
        {
            return GetDisplayValue( value,  enumType, string.Empty);
        }

        /**/
        /// <summary>
        ///   Gets the <see cref="EnumItemDescriptionAttribute"/> for special enum value: <paramref name="value"/>,
        ///   special enum type: <paramref name="enumType"/> and special culture.
        /// </summary>
        /// <param name="value">the value of special enum</param>
        /// <param name="enumType">the enum type of special enum</param>
        /// <param name="culture">special culture for item description.</param>
        /// <returns>The attributes attatched on special enum value.</returns>
        public static string GetDisplayValue(object value, Type enumType, string culture)
        {
            if (enumType == null || !enumType.IsEnum)
            {
                throw new NotSupportedException("enumType is not a Enum");
            }
            if (value == null || !Enum.IsDefined(enumType, value))
            {
                throw new ArgumentException("value is not defined in " + enumType.FullName);
            }

            
            if (string.IsNullOrEmpty(culture))
            {
                CultureInfo currentUICulute = Thread.CurrentThread.CurrentUICulture;
                culture = currentUICulute.Name;
            }

            if (culture == null)
            {
                return value.ToString();
            }

            Dictionary<string, EnumItemDescriptionAttribute> disc = GetDisplayValuesImp(value, enumType);
            if (disc.ContainsKey(culture))
            {
                return disc[culture].Description;
            }

            return value.ToString();
        }

        /**/
        /// <summary>
        ///   Gets all <see cref="EnumItemDescriptionAttribute"/>
        ///   for special enum value: <paramref name="value"/> and special enum type: <paramref name="enumType"/>.
        /// </summary>
        /// <param name="value">the value of special enum</param>
        /// <param name="enumType">the enum type of special enum</param>
        /// <returns>All attributes attatched on special enum value.</returns>
        private static Dictionary<string, EnumItemDescriptionAttribute> GetDisplayValuesImp(object value, Type enumType)
        {
            if (!_cache.ContainsKey(enumType))
            {
                Dictionary<object, Dictionary<string, EnumItemDescriptionAttribute>> displayValues =
                    new Dictionary<object, Dictionary<string, EnumItemDescriptionAttribute>>();
                foreach (Enum item in Enum.GetValues(enumType))
                {
                    Dictionary<string, EnumItemDescriptionAttribute> descriptions =
                        new Dictionary<string, EnumItemDescriptionAttribute>();
                    FieldInfo enumField = enumType.GetField(item.ToString());
                    if (enumField != null)
                    {
                        foreach (EnumItemDescriptionAttribute desc
                            in enumField.GetCustomAttributes(typeof(EnumItemDescriptionAttribute), true))
                        {
                            descriptions.Add(desc.DisplaySerialName, desc);
                        }
                    }
                    displayValues.Add(item, descriptions);
                }
                _cache.Add(enumType, displayValues);
            }

            return _cache[enumType][value];
        }
    }
}
